Repository navigation
Codex/vae pyhealth2 - #1358
Codex/vae pyhealth2#1358dalloliogm wants to merge 20 commits into
Conversation
Test CoverageTotal: 77.45% (24533 / 31676 lines)
|
|
/review |
|
If the PR is too large, I can split it into two, so the AI review can take place. The changes to the VAE class are small. Most of the new lines come from the notebooks included to describe what is a VAE and how it can be used. I could split those into a different PR |
|
Yes, please split it if possible. |
|
I think the main thing is the .ipynb notebooks are massive, so just one with a simple examples *.py file and then the follow up notebooks would be much appreciated. |
|
Hi, I think the problem was that I forgot to clean up the notebook outputs. I've done that now - can you try reviewing again? There were a few plots that made the notebooks very big - but they should not have been committed. |
|
/review |
There was a problem hiding this comment.
Previous review
AI Review
suggestion: need change
decision: human review
Thank you for coming back to the VAE bounty and porting it to PyHealth 2.0. Adding categorical EHR sequence support, optional label conditioning for images, and three example notebooks is a useful extension. Line coverage for pyhealth/models/vae.py is 88.29%, above the 80% bar, and total repository coverage is 74.07%, above the 60% bar.
A maintainer will also take a look. The change alters the public contract of the existing VAE: hidden_dim changes meaning and default, latent_dim is new, and the generate signature changes. Existing correct calls can therefore break or behave differently, and a maintainer should decide whether that is acceptable.
To get this merged:
- Single-label image datasets. The constructor auto-uses a sole output label as the condition. A dataset with a single multilabel or regression output, for example ChestXray14 multilabel, now raises an error, where the old version ignored labels. Only auto-condition when the label is binary or multiclass, and otherwise ignore it unless
condition_keyis given explicitly. generate(num_samples=...)on labeled image datasets. This now raises because of the auto-conditioning. Please keep unconditional generation working, for example by sampling the condition at random or only conditioning whencondition_keyis passed explicitly.@torch.no_grad()removed fromgenerate. The returned images now require grad, so.numpy()and similar calls fail. Please restore the decorator or detach the output. The olddeviceargument has also been dropped, and the second positional argument is nowsequence_length. Please keep the signature backward compatible or call the change out clearly.hidden_dimsemantics. Previouslyhidden_dimwas the latent size, with a default of 128. It is now the channel width, with a default of 32. ExistingVAE(dataset, hidden_dim=N)calls silently build a different network. Please keephidden_dimas the latent size, or document the break prominently in the description and docs.- Tests. The
COVID19CXRImageGenerationtask tests and the learning test were deleted, although that task still ships. Please restore tests for the task and a test that training reduces the reconstruction error. Also add tests for the new error branches and forTrainer.train.
Optional suggestions:
- In the ChestXray14 notebook, the comment about patient-level splits being streaming subsets looks inaccurate.
split_by_patientreturns subsets thatget_dataloadershould accept, so you could use it directly. - Unsupported PIL modes such as "P" raise a bare
KeyErrorat the mode lookup in_init_image. A clearValueErrorwould be friendlier.
Reviewed commit 3e0ff51
|
/review |
|
/review |
There was a problem hiding this comment.
Previous review
AI Review
suggestion: need change
decision: human review
Thank you for this contribution. It extends the PyHealth 2.0 VAE to categorical EHR code sequences and optional label-conditioned image generation, and it adds three example notebooks and docs updates. The new model code looks reasonable. A maintainer will also take a look, because the rewrite of pyhealth/models/vae.py changes the existing VAE's public interface and the format of saved checkpoints.
Coverage: pyhealth/models/vae.py is at 89.05%, above the 80% bar for modified files, and the repository total is 74.11%, above the 60% bar. I did not run the tests or notebooks.
To get this merged:
- Keep backward compatibility, or state the break clearly. The old
encode()anddecode()methods are gone. The attributesencoder,decoder,muandlogvarare renamed, and the ResBlock2D architecture is replaced. State_dicts saved from the previous VAE therefore no longer load. Please restoreencodeanddecode(or add thin wrappers). If the architecture change is intentional, document it in the PR description and the docs as a breaking change. - Image processors without an explicit
modeused to work, because the channel count was read from the data. They now raise a ValueError. Please infer the channel count from a sample whenmodeis None, or document this as an intentional break. Also keep a clear error forto_tensor=False, which the oldto_tensorcheck used to catch. - Restore the test coverage that was removed. The multi-epoch "learns to reconstruct" test is now a single optimizer step. The RGB, gradient-flow and state_dict round-trip tests are gone, so the RGB path used by the COVID classification example is untested. Please add at least an RGB forward-pass test and a state_dict round-trip test.
Optional suggestions:
- The sequence path always samples the latent, even in eval mode, so validation loss is noisy. Using
muwhennot self.trainingwould make it deterministic. - In the synthetic notebook,
torch.manual_seedbefore an eval-mode forward has no effect, so the remark about seeding can be dropped. - The ChestXray14 notebook subclasses the binary task only to add
patient_id. That would be better placed in the task itself.
Reviewed commit 478feab
|
I just noticed that the image-generation part of this bounty was already covered by PR #1351. Thanks for getting that implementation merged. This PR builds on that work by extending the VAE to categorical EHR sequences, alongside image inputs. It adds examples for synthetic image generation, conditional image generation with ChestXray14, and EHR sequence modeling, and expands the tests to cover reconstruction, generation, and Trainer compatibility across both input types. I’ve kept image conditioning opt-in and preserved the existing image VAE behavior and COVID-19 CXR task from #1351. The current CI checks are passing. My aim is to make this a complementary extension to #1351, rather than duplicate its image-generation work. |
|
/review |
|
/review |
e73ff9a to
bed99d7
Compare
There was a problem hiding this comment.
Previous review
AI Review
suggestion: need change
decision: human review
Thank you for this contribution. It extends the PyHealth 2.0 VAE with optional label conditioning for images and a new categorical EHR sequence VAE. It also adds three example notebooks, documentation updates and tests. It keeps the earlier image VAE's module names and state_dict keys, which is a nice touch.
A maintainer will take a look as well. The change modifies pyhealth/trainer.py, a shared base class, so Trainer.inference() now behaves differently for any model that returns mask and reconstruction_logits. That is a core-level change that needs a human decision.
To get this merged:
- In
VAE.generate()for sequence VAEs, the latent is sampled on thedeviceargument. Thelatent_to_hidden,sequence_decoderandsequence_outputlayers live on the model's device. If the two differ,generate(device=...)will fail. Either move the latent toself.device, or document thatdevicemust match the model's device. Please add a test for this path.
Coverage: vae.py is at 87.0% and chestxray14_binary_classification.py is at 100%, both above the 80% bar. trainer.py is at 77.2%, just under 80%, and the whole repository is at 75.6%, above the 60% bar. The trainer.py shortfall mostly comes from existing code. Still, the new variable-length branch in inference() is tested, so it would be good to confirm that it is covered.
Optional suggestions:
- The image forward pass no longer accepts a list of image tensors, as the previous version did. Consider restoring that or mentioning the change in the docs.
Trainer.inference()stores full per-token probability arrays for sequence VAEs. With large EHR vocabularies this could use a lot of memory, so a note in the docs may help.- In the ChestXray14 notebook, the
elif not DATA_AVAILABLEbranch is unreachable, becauseDOWNLOAD_DATAis alreadynot DATA_AVAILABLE. The notebook also starts a roughly 5k-image download without asking, so a clearer warning may help. - Conditioning is tested only for output shape. A test showing that different conditions change the output would strengthen it.
- The synthetic notebook describes training results but has no saved outputs.
Reviewed commit bed99d7
|
/review |
jhnwu3
left a comment
There was a problem hiding this comment.
tldr; The task schema that gets sent and attributed to the model should be a better pick than a custom inference_output_mode attribute here.
| if per_step: | ||
| if sequence_reconstruction: | ||
| # Sequence reconstruction is not a classification task, but | ||
| # inference can still return each sample's unpadded tokens |
There was a problem hiding this comment.
Is it possible to instead of creating a custom "infernece_output_mode" we simply reference the model's output schema here to check that it's a sequence processor here for this change here?
| mask = tokens != self.pad_token | ||
| lengths = mask.sum(dim=1).clamp(min=1).cpu() | ||
| embeddings = self.token_embedding(tokens) | ||
| packed = pack_padded_sequence( |
There was a problem hiding this comment.
[low] Zero-width code batches fail in pack_padded_sequence
If every sequence in a batch is empty, the padded token tensor has width 0. The lengths are clamped to 1 at line 367, which exceeds that width, so pack_padded_sequence raises. Fix: pad the token tensor to at least one column (for example with F.pad) before packing, or return a zero loss for an all-empty batch. This is non-blocking because it needs every patient in a batch to have no codes.
| start = self.sequence_start.expand(tokens.shape[0], -1, -1) | ||
| if tokens.shape[1] > 1: | ||
| previous_embeddings = self.token_embedding(tokens[:, :-1]) | ||
| decoder_inputs = torch.cat((start, previous_embeddings), dim=1) |
There was a problem hiding this comment.
[low] Teacher-forced decoder may ignore the latent (posterior collapse)
The decoder receives the ground-truth previous tokens as inputs, so each token can be predicted from its predecessor alone. The latent only sets the initial GRU hidden state. The KL term can then collapse the posterior, and samples drawn from the prior behave like an unconditioned token language model. Consider feeding the latent at every decoder step, or applying word dropout to the decoder inputs, and say in the docstring that the latent may be weakly used. Non-blocking, since the reconstruction and generation API still work as documented.
| ) | ||
| kl_loss = kl_per_sequence.mean() | ||
| loss = reconstruction_loss + kl_loss | ||
| token_probabilities = F.softmax(logits, dim=-1) |
There was a problem hiding this comment.
[low] Full token probabilities are computed on every training forward
F.softmax over the B x L x V logits runs in every forward pass, including training, where only the loss is used. That allocates a large tensor each step. Only Trainer.inference needs y_prob, so compute the probabilities there, or return logits and apply softmax in the inference path. Non-blocking; it costs memory and time but is correct.
| samples.append({"image": event["path"], "label": int(event[self.disease])}) | ||
| samples.append( | ||
| { | ||
| "patient_id": patient.patient_id, |
There was a problem hiding this comment.
[low] patient_id added to the binary task only
The new patient_id key is added only to ChestXray14BinaryClassification. ChestXray14MultilabelClassification does not emit it, so split_by_patient cannot group patients for that task. Task outputs are also cached by the task's vars(), so caches written before this change will not gain the key until they are regenerated. Add the key to the multilabel task too, or note the cache behavior in the change description. Non-blocking, since the notebook only uses the binary task.
There was a problem hiding this comment.
AI Review
suggestion: approve
decision: human review
Thank you for this contribution, and for coming back to the VAE bounty. It extends the PyHealth 2.0 VAE with a categorical EHR code-sequence path (GRU encoder, autoregressive decoder and generation) and optional label conditioning for image VAEs. It also adds three example notebooks and the matching docs updates. The image API is kept, including encode/decode, hidden_dim and the state_dict keys.
The diff looks correct and complete to me, and it matches the description. Tests in tests/core/test_vae.py cover image and sequence forward passes, generation, conditioning errors, Trainer evaluate and inference, and a state_dict round trip. Coverage for pyhealth/models/vae.py is 88.5%, above the 80% bar. The repository total is 76.98%, above 60%. pyhealth/trainer.py is at 77.3%, slightly under 80%, but that file is mostly pre-existing code and your added branch is exercised by the tests.
A maintainer will take a look before this merges. The change adds a new branch to Trainer.inference in pyhealth/trainer.py, which is shared core code. The branch only runs for models that set the new inference_output_mode attribute, so existing models should behave the same. It still deserves a human check.
Optional suggestions:
- A batch where every code sequence is empty would fail in pack_padded_sequence. A guard would avoid that.
- The teacher-forced decoder can ignore the latent (posterior collapse). KL annealing or input dropout could help later.
- Trainer.train on a sequence model isn't covered by a test. The notebook does it, but the notebooks aren't run in CI.
- patient_id was added only to the binary ChestXray14 task. The multilabel task may want it too.
Reviewed commit 8706f9a
Hello,
Almost one year ago I started contributing to the VAE bounty in PyHealth. Unfortunately, I messed up with Git and became busy with work, and time passed, so I never followed up on the review comments on my previous PR.
This is a new implementation of the bounty for PyHealth 2.0.
Contributor: Giovanni Dall’Olio
Contribution Type: Model
Description: The VAE now supports image inputs and categorical EHR code sequences. I’ve also added or updated three notebooks demonstrating synthetic image generation, ChestXray14 image modeling, and EHR sequence modeling, with explanations of potential applications.
Files to Review: